// ball_log.h                                                         -*-C++-*-
#ifndef INCLUDED_BALL_LOG
#define INCLUDED_BALL_LOG

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide macros and utility functions to facilitate logging.
//
//@CLASSES:
//  ball::Log: namespace for logging utilities (for *internal* use only)
//
//@MACROS:
//  BALL_LOG_SET_CATEGORY(C): set a category for logging to the specified `C`
//  BALL_LOG_SET_DYNAMIC_CATEGORY(C): set a run-time-dependent category
//  BALL_LOG_SET_CLASS_CATEGORY(C): set a category in the scope of a class
//  BALL_LOG_SET_NAMESPACE_CATEGORY(C): set a category in a namespace
//  BALL_LOG_SET_CATEGORY_HIERARCHICALLY(C): set a category hierarchically
//  BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(C): set a run-time category
//  BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(C): set a class category
//  BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(C): set a namespace category
//  BALL_LOG_TRACE: produce a log record with the `e_TRACE` severity level
//  BALL_LOG_DEBUG: produce a log record with the `e_DEBUG` severity level
//  BALL_LOG_INFO: produce a log record with the `e_INFO` severity level
//  BALL_LOG_WARN: produce a log record with the `e_WARN` severity level
//  BALL_LOG_ERROR: produce a log record with the `e_ERROR` severity level
//  BALL_LOG_FATAL: produce a log record with the `e_FATAL` severity level
//  BALL_LOG_STREAM(SEV): produce a log record with the specified `SEV` level
//  BALL_LOGCB_TRACE(CB): produce a `e_TRACE` log record using callback `CB`
//  BALL_LOGCB_DEBUG(CB): produce a `e_DEBUG` log record using callback `CB`
//  BALL_LOGCB_INFO(CB): produce an `e_INFO` log record using callback `CB`
//  BALL_LOGCB_WARN(CB): produce a `e_WARN` log record using callback `CB`
//  BALL_LOGCB_ERROR(CB): produce an `e_ERROR` log record using callback `CB`
//  BALL_LOGCB_FATAL(CB): produce a `e_FATAL` log record using callback `CB`
//  BALL_LOGCB_STREAM(SEV, CB): produce a `SEV` log record using callback
//  BALL_LOGVA_TRACE(MSG, ...): produce `e_TRACE` record using `printf` format
//  BALL_LOGVA_DEBUG(MSG, ...): produce `e_DEBUG` record using `printf` format
//  BALL_LOGVA_INFO( MSG, ...): produce `e_INFO` record using `printf` format
//  BALL_LOGVA_WARN( MSG, ...): produce `e_WARN` record using `printf` format
//  BALL_LOGVA_ERROR(MSG, ...): produce `e_ERROR` record using `printf` format
//  BALL_LOGVA_FATAL(MSG, ...): produce `e_FATAL` record using `printf` format
//  BALL_LOGVA(SEV, MSG, ...): produce a `SEV` log record using `printf` format
//  BALL_LOG_TRACE_BLOCK: set code block with `e_TRACE` condition of execution
//  BALL_LOG_DEBUG_BLOCK: set code block with `e_DEBUG` condition of execution
//  BALL_LOG_INFO_BLOCK: set a code block with `e_INFO` condition of execution
//  BALL_LOG_WARN_BLOCK: set a code block with `e_WARN` condition of execution
//  BALL_LOG_ERROR_BLOCK: set code block with `e_ERROR` condition of execution
//  BALL_LOG_FATAL_BLOCK: set code block with `e_FATAL` condition of execution
//  BALL_LOG_STREAM_BLOCK(SEV): set a code block with `SEV` condition
//  BALL_LOGCB_TRACE_BLOCK(CB): set `e_TRACE` block with the specified callback
//  BALL_LOGCB_DEBUG_BLOCK(CB): set `e_DEBUG` block with the specified callback
//  BALL_LOGCB_INFO_BLOCK(CB): set `e_INFO` block with the specified callback
//  BALL_LOGCB_WARN_BLOCK(CB): set `e_WARN` block with the specified callback
//  BALL_LOGCB_ERROR_BLOCK(CB): set an `e_ERROR` block with the specified `CB`
//  BALL_LOGCB_FATAL_BLOCK(CB): set `e_FATAL` block with the specified callback
//  BALL_LOGCB_STREAM_BLOCK(SEV, CB): set a `SEV` block with the specified `CB`
//  BALL_LOG_IS_ENABLED(SEV): indicate if `SEV` is severe enough for logging
//
//@SEE_ALSO: ball_loggermanager, ball_category, ball_severity, ball_record
//
//@DESCRIPTION: This component provides preprocessor macros and utility
// functions to facilitate use of the `ball_loggermanager` component.  In
// particular, the macros defined herein greatly simplify the mechanics of
// generating log records.  The utility functions provided in `ball::Log` are
// intended only for use by the macros and should *not* be called directly.
//
// The macros defined herein pertain to the logger manager singleton only, and
// not to any non-singleton instances of `ball::LoggerManager`.  In particular,
// the macros do not have any effect unless the logger manager singleton is
// initialized.  Note that the flow of control may pass through a use of any of
// the macros *before* the logger manager singleton has been initialized or
// *after* it has been destroyed; however, control should not pass through any
// macro use *during* logger manager singleton initialization or destruction.
// See {`ball_loggermanager`|Logger Manager Singleton Initialization} for
// details on the recommended procedure for initializing the singleton.
//
///Thread Safety
///-------------
// All macros defined in this component are thread-safe, and can be invoked
// concurrently by multiple threads.
//
// Additionally, each use of a logging macro will create a distinct log record,
// and `ball::Observer` implementations (like those in `ball`) generally
// guarantee that output for different log records are not interleaved.
//
///Macro Reference
///---------------
// This section documents the preprocessor macros defined in this component.
//
// The first three macros described below are used to define categories, at
// either block scope or class scope, to which records are logged by the C++
// stream-based and `printf`-style logging macros (described further below).
//
///Macros for Defining Categories at Block Scope
///- - - - - - - - - - - - - - - - - - - - - - -
// The following two macros are used to establish logging categories that have
// block scope:
//
//: `BALL_LOG_SET_CATEGORY(CATEGORY)`:
//:     Set a category for logging to the specified `CATEGORY` (assumed to be
//:     of type convertible to `const char *`).  On the *first* invocation of
//:     this macro in a code block, the `ball::Log::setCategory` method is
//:     invoked to retrieve the address of an appropriate category structure
//:     for its scope; subsequent invocations will use a cached address of the
//:     category.  (See the function-level documentation of
//:     `ball::Log::setCategory` for more information.)  This macro must be
//:     used at block scope, and can be used at most once in any given block
//:     (or else a compiler diagnostic will result).
//:
//: `BALL_LOG_SET_DYNAMIC_CATEGORY(CATEGORY)`:
//:     Set, *on* *EACH* *invocation*, a category for logging to the specified
//:     `CATEGORY` (assumed to be of type convertible to `const char *`).  On
//:     *EVERY* invocation of this macro in a code block, the
//:     `ball::Log::setCategory` method is invoked to retrieve the address of
//:     an appropriate category structure for its scope; the address returned
//:     from `ball::Log::setCategory` is *NOT* cached for subsequent calls.
//:     (See the function-level documentation of `ball::Log::setCategory` for
//:     more information.)  This macro must be used at block scope and can be
//:     used at most once in any given block (or else a compiler diagnostic
//:     will result).  Note that this macro should be used to create categories
//:     that depend on *RUN-TIME* values only (e.g., LUW or UUID).
//
// There can be at most one use of either `BALL_LOG_SET_CATEGORY` or
// `BALL_LOG_SET_DYNAMIC_CATEGORY` in any given block (or else a compiler
// diagnostic will result).  Note that categories that are set using these
// macros, including dynamic categories, are not destroyed until the logger
// manager singleton is destroyed.
//
///Macro for Defining Categories at Class Scope
/// - - - - - - - - - - - - - - - - - - - - - -
// The following macro is used to establish logging categories that have class
// scope:
//
//: `BALL_LOG_SET_CLASS_CATEGORY(CATEGORY)`:
//:     Set a category for logging to the specified `CATEGORY` (assumed to be
//:     of type convertible to `const char *`) in the scope of the class within
//:     which this macro is used.  Similar to `BALL_LOG_SET_CATEGORY`, the
//:     category is set *once* only, the first time that it is accessed (i.e.,
//:     it is not a dynamic category).  This macro must be used, at most once,
//:     within the definition of a class or class template (or else a compiler
//:     diagnostic will result).  Note that use of this macro may occur in
//:     either a `public`, `private`, or `protected` section of a class's
//:     interface, although `private` should be preferred.
//
// Note that similar to block-scope categories (see `BALL_LOG_SET_CATEGORY` and
// `BALL_LOG_SET_DYNAMIC_CATEGORY`), class-scope categories are not destroyed
// until the logger manager singleton is destroyed.
//
///Macro for Defining Categories at Namespace or Global Scope
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The following macro is used to establish logging categories that have
// namespace or global scope:
//
//: `BALL_LOG_SET_NAMESPACE_CATEGORY(CATEGORY)`:
//:     Set a category for logging to the specified `CATEGORY` (assumed to be
//:     of type convertible to `const char *`) in the namespace (or global)
//:     scope within which this macro is used.  Similar to
//:     `BALL_LOG_SET_CATEGORY`, the category is set *once* only, the first
//:     time that it is accessed (i.e., it is not a dynamic category).  This
//:     macro may be used, in `.cpp` files *only*, at most once in any given
//:     namespace and at most once at global scope (or else a compiler
//:     diagnostic will result).  Do *NOT* use this macro in `.h` files.
//
// Note that similar to block-scope categories (see `BALL_LOG_SET_CATEGORY` and
// `BALL_LOG_SET_DYNAMIC_CATEGORY`), namespace-scope categories are not
// destroyed until the logger manager singleton is destroyed.
//
///Macros for Defining Hierarchical Categories
///- - - - - - - - - - - - - - - - - - - - - -
// The following macros are used to establish logging categories having
// thresholds that are given by the existing non-default category whose name
// matches the longest prefix of the new category's name (i.e., the threshold
// levels of new categories are determined "hierarchically" from existing
// categories rather than from the default threshold levels of the logger
// manager singleton).
//
//: `BALL_LOG_SET_CATEGORY_HIERARCHICALLY(CATEGORY)`
//:     Set a category for logging to the specified `CATEGORY` (assumed to be
//:     of type convertible to `const char *`).  On the *first* invocation of
//:     this macro in a code block, the `ball::Log::setCategoryHierarchically`
//:     method is invoked to retrieve the address of an appropriate category
//:     structure for its scope; subsequent invocations will use a cached
//:     address of the category.  (See the function-level documentation of
//:     `ball::Log::setCategoryHierarchically` for more information.)  This
//:     macro must be used at block scope, and can be used at most once in any
//:     given block (or else a compiler diagnostic will result).
//:
//: `BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(CATEGORY)`:
//:     Set, *on* *EACH* *invocation*, a category for logging to the specified
//:     `CATEGORY` (assumed to be of type convertible to `const char *`).  On
//:     *EVERY* invocation of this macro in a code block, the
//:     `ball::Log::setCategoryHierarchically` method is invoked to retrieve
//:     the address of an appropriate category structure for its scope; the
//:     address returned from `ball::Log::setCategoryHierarchically` is *NOT*
//:     cached for subsequent calls.  (See the function-level documentation of
//:     `ball::Log::setCategoryHierarchically` for more information.)  This
//:     macro must be used at block scope and can be used at most once in any
//:     given block (or else a compiler diagnostic will result).  Note that
//:     this macro should be used to create categories that depend on
//:     *RUN-TIME* values only (e.g., LUW or UUID).
//:
//: `BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(CATEGORY)`
//:     Set a category for logging to the specified `CATEGORY` (assumed to be
//:     of type convertible to `const char *`) in the scope of the class within
//:     which this macro is used.  Similar to
//:     `BALL_LOG_SET_CATEGORY_HIERARCHICALLY`, the category is set *once*
//:     only, using the `ball::Log::setCategoryHierarchically` function, the
//:     first time that it is accessed (i.e., it is not a dynamic category).
//:     (See the function-level documentation for
//:     `ball::Log::setCategoryHierarchically` for more information.)  This
//:     macro must be used, at most once, within the definition of a class or
//:     class template (or else a compiler diagnostic will result).  Note that
//:     use of this macro may occur in either a `public`, `private`, or
//:     `protected` section of a class's interface, although `private` should
//:     be preferred.
//:
//: `BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(CATEGORY)`
//:     Set a category for logging to the specified `CATEGORY` (assumed to be
//:     of type convertible to `const char *`) in the namespace (or global)
//:     scope within which this macro is used.  Similar to
//:     `BALL_LOG_SET_CATEGORY_HIERARCHICALLY`, the category is set *once*
//:     only, using the `ball::Log::setCategoryHierarchically` function, the
//:     first time that it is accessed (i.e., it is not a dynamic category).
//:     (See the function-level documentation for
//:     `ball::Log::setCategoryHierarchically` for more information.)  This
//:     macro may be used, in `.cpp` files *only*, at most once in any given
//:     namespace and at most once at global scope (or else a compiler
//:     diagnostic will result).  Do *NOT* use this macro in `.h` files.
//
///Macros for Logging Records
/// - - - - - - - - - - - - -
// The macros defined in this subsection are the ones that are actually used to
// produce log records.  A use of any one of the logging macros requires that a
// logging category (as established by the macros defined above) be in scope at
// the point where the macro is used.  Note that the formatted string that is
// generated for the message attribute of each log record includes the category
// that is in scope and the filename as established by the standard `__FILE__`
// macro.
//
// The code within any logging statement/code block must not produce any side
// effects because it may or may not be executed based on run-time
// configuration of the `ball` logging subsystem:
// ```
// BALL_LOG_INFO << ++i;    // (!) May or may not be incremented
//
// BALL_LOG_TRACE_BLOCK {
//    processRequest(...);  // (!) May or may not be called
// }
// ```
//
// A set of macros based on C++ streams, `BALL_LOG_TRACE`, `BALL_LOG_DEBUG`,
// `BALL_LOG_INFO`, `BALL_LOG_WARN`, `BALL_LOG_ERROR`, and `BALL_LOG_FATAL`,
// are the ones most commonly used for logging.  They have the following usage
// pattern:
// ```
// BALL_LOG_TRACE << X << Y ... ;
// BALL_LOG_DEBUG << X << Y ... ;
// BALL_LOG_INFO  << X << Y ... ;
// BALL_LOG_WARN  << X << Y ... ;
// BALL_LOG_ERROR << X << Y ... ;
// BALL_LOG_FATAL << X << Y ... ;
//     where X, Y, ... represents any sequence of values for which
//     `operator<<` is defined.  The resulting formatted message string is
//     logged with the severity indicated by the name of the macro
//     (e.g., `BALL_LOG_TRACE` logs with severity `ball::Severity::e_TRACE`).
// ```
// A closely-related macro also based on C++ streams, `BALL_LOG_STREAM`,
// requires that the severity be explicitly supplied as an argument:
// ```
// BALL_LOG_STREAM(SEVERITY) << X << Y ... ;
//     where X, Y, ... represents any sequence of values for which
//     `operator<<` is defined.  The resulting formatted message string is
//     logged with the specified `SEVERITY`.
// ```
// Another set of macros based on C++ streams, similar to `BALL_LOG_TRACE`,
// etc., allow the caller to specify a "callback" function that is passed the
// `ball::UserFields *` used to represent the user fields of a log record.
// `BALL_LOGCB_TRACE`, `BALL_LOGCB_DEBUG`, `BALL_LOGCB_INFO`,
// `BALL_LOGCB_WARN`, `BALL_LOGCB_ERROR`, and `BALL_LOGCB_FATAL` have the
// following usage pattern:
// ```
// BALL_LOGCB_TRACE(CALLBACK) << X << Y ... ;
// BALL_LOGCB_DEBUG(CALLBACK) << X << Y ... ;
// BALL_LOGCB_INFO(CALLBACK)  << X << Y ... ;
// BALL_LOGCB_WARN(CALLBACK)  << X << Y ... ;
// BALL_LOGCB_ERROR(CALLBACK) << X << Y ... ;
// BALL_LOGCB_FATAL(CALLBACK) << X << Y ... ;
//     where X, Y, ... represents any sequence of values for which
//     `operator<<` is defined and `CALLBACK` is a callback taking a
//     `ball::UserFields *` as an argument.  The resulting formatted message
//     string is logged with the severity indicated by the name of the macro
//     (e.g., `BALL_LOGCB_ERROR` logs with severity
//     `ball::Severity::e_ERROR`).  The generated log record will contain the
//     `ball::UserFields` representing user fields as populated by `CALLBACK`.
//     Note that the callback supplied to the logging macro must match the
//     prototype `void (*)(ball::UserFields *)`.
// ```
// A closely-related macro also based on C++ streams, `BALL_LOGCB_STREAM`,
// requires that the severity be explicitly supplied as an argument:
// ```
// BALL_LOGCB_STREAM(SEVERITY, CALLBACK) << X << Y ... ;
//     where X, Y, ... represents any sequence of values for which
//     `operator<<` is defined.  The resulting formatted message string is
//     logged with the specified `SEVERITY`.  The generated log record will
//     contain the `ball::UserFields` representing user fields as populated by
//     `CALLBACK`.  Note that the callback supplied to the logging macro must
//     match the prototype `void (*)(ball::UserFields *)`.
// ```
// The remaining macros are based on `printf`-style format specifications:
// ```
// BALL_LOGVA_TRACE(MSG, ...);
// BALL_LOGVA_DEBUG(MSG, ...);
// BALL_LOGVA_INFO( MSG, ...);
// BALL_LOGVA_WARN( MSG, ...);
// BALL_LOGVA_ERROR(MSG, ...);
// BALL_LOGVA_FATAL(MSG, ...);
//     Format the specified `...` optional arguments, if any, according to the
//     `printf`-style format specification in the specified `MSG` (assumed to
//     be of type convertible to `const char *`) and log the resulting
//     formatted message string with the severity indicated by the name of the
//     macro (e.g., `BALL_LOGVA_INFO` logs with severity
//     `ball::Severity::e_INFO`).  The behavior is undefined unless the number
//     and types of optional arguments are compatible with the format
//     specification in `MSG`.  Note that each use of these macros must be
//     terminated by a `;`.
// ```
// A closely-related `printf`-style macro, `BALL_LOGVA`, requires that the
// severity be explicitly supplied as an argument:
// ```
// BALL_LOGVA(SEVERITY, MSG, ...);
//     Format the specified `...` optional arguments, if any, according to the
//     `printf`-style format specification in the specified `MSG` (assumed to
//     be of type convertible to `const char *`) and log the resulting
//     formatted message string with the specified `SEVERITY`.  The behavior
//     is undefined unless the number and types of optional arguments are
//     compatible with the format specification in `MSG`.  Note that each use
//     of this macro must be terminated by a `;`.
// ```
//
///Macros for Logging Code Blocks
/// - - - - - - - - - - - - - - -
//  The following macros allow the caller to start a code block that will be
//  conditionally executed depending on the current logging threshold of the
//  category that is in scope of those macros:
// ```
// BALL_LOG_TRACE_BLOCK { ... }
// BALL_LOG_DEBUG_BLOCK { ... }
// BALL_LOG_INFO_BLOCK  { ... }
// BALL_LOG_WARN_BLOCK  { ... }
// BALL_LOG_ERROR_BLOCK { ... }
// BALL_LOG_FATAL_BLOCK { ... }
// ```
// A closely-related macro, `BALL_LOG_STREAM_BLOCK`, requires that the severity
// be explicitly supplied as an argument:
// ```
// BALL_LOG_STREAM_BLOCK(SEVERITY) { ... }
// ```
// Another set of macros, similar to `BALL_LOG_*_BLOCK`, allow the caller to
// specify a "callback" function that is passed the `ball::UserFields *` used
// to represent the user fields of a log record:
// ```
// BALL_LOGCB_TRACE_BLOCK(CALLBACK) { ... }
// BALL_LOGCB_DEBUG_BLOCK(CALLBACK) { ... }
// BALL_LOGCB_INFO_BLOCK(CALLBACK)  { ... }
// BALL_LOGCB_WARN_BLOCK(CALLBACK)  { ... }
// BALL_LOGCB_ERROR_BLOCK(CALLBACK) { ... }
// BALL_LOGCB_FATAL_BLOCK(CALLBACK) { ... }
// ```
// A closely-related macro, `BALL_LOGCB_STREAM_BLOCK`, requires that the
// severity be explicitly supplied as an argument:
// ```
// BALL_LOGCB_STREAM_BLOCK(SEVERITY, CALLBACK) { ... }
// ```
//  Within the logging code block a special macro, `BALL_LOG_OUTPUT_STREAM`,
//  provides access to the log stream.
//
///Utility Macros
/// - - - - - - -
// The following utility macro is intended for special-purpose use for
// fine-tuning logging behavior.  A use of this macro requires that a logging
// category (as established by the macros defined above) be in scope at the
// point where the macro is used.
// ```
// BALL_LOG_IS_ENABLED(SEVERITY)
//     Return `true` if the specified `SEVERITY` is at least as severe as any
//     of the threshold levels of the logging category that is in scope, and
//     `false` otherwise.
// ```
//
///Usage
///-----
// The following code fragments illustrate the standard pattern of macro usage.
//
///Example 1: A Basic Logging Example
/// - - - - - - - - - - - - - - - - -
// The following trivial example shows how to use the logging macros to log
// messages at various levels of severity.
//
// First, we initialize the log category within the context of this function.
// The logging macros such as `BALL_LOG_ERROR` will not compile unless a
// category has been specified in the current lexical scope:
// ```
// BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY");
// ```
// Then, we record messages at various levels of severity.  These messages will
// be conditionally written to the log depending on the current logging
// threshold of the category (configured using the `ball::LoggerManager`
// singleton):
// ```
// BALL_LOG_FATAL << "Write this message to the log if the log threshold "
//                << "is above 'ball::Severity::e_FATAL' (i.e., 32).";
//
// BALL_LOG_TRACE << "Write this message to the log if the log threshold "
//                << "is above 'ball::Severity::e_TRACE' (i.e., 192).";
// ```
// Next, we demonstrate how to use proprietary code within logging macros.
// Suppose you want to add the content of a vector to the log trace:
// ```
// bsl::vector<int> myVector(4, 328);
// BALL_LOG_TRACE_BLOCK {
//     BALL_LOG_OUTPUT_STREAM << "myVector = [ ";
//     unsigned int position = 0;
//     for (bsl::vector<int>::const_iterator it  = myVector.begin(),
//                                           end = myVector.end();
//         it != end;
//         ++it, ++position) {
//         BALL_LOG_OUTPUT_STREAM << position << ':' << *it << ' ';
//     }
//     BALL_LOG_OUTPUT_STREAM << ']';
// }
// ```
// Note that the code block will be conditionally executed depending on the
// current logging threshold of the category.  The code within the block must
// not produce any side effects, because its execution depends on the current
// logging configuration.  The special macro `BALL_LOG_OUTPUT_STREAM` provides
// access to the log stream within the block.
//
///Example 2: Setting the Current Log Category
///- - - - - - - - - - - - - - - - - - - - - -
// This example provides more detail on setting the log category in the
// current lexical scope.  The following macro instantiation sets the category
// for logging to be "EQUITY.NASD" in the enclosing lexical scope:
// ```
// BALL_LOG_SET_CATEGORY("EQUITY.NASD")
// ```
// Note that this macro must not be used at file scope and it can be used at
// most once in any given block (or else a compiler diagnostic will result).  A
// different category may be established to override one that is in effect, but
// it must occur in a nested scope.  In any case, a use of this macro (or of
// `BALL_LOG_SET_DYNAMIC_CATEGORY`) must be visible from within the lexical
// scope of every use of the log-generating macros.  The following fragment of
// code shows how to set a different category in a nested inner block that
// hides a category set in an enclosing block:
// ```
// void logIt()
// {
//     BALL_LOG_SET_CATEGORY("EQUITY.NASD")
//
//     // Logging to category "EQUITY.NASD" unless overridden in a nested
//     // block.
//     // [*] ...
//
//     {
//         // [*] ...
//         // Still logging to category "EQUITY.NASD".
//
//         BALL_LOG_SET_CATEGORY("EQUITY.NASD.SUNW")
//
//         // Now logging to category "EQUITY.NASD.SUNW".
//         // [*] ...
//     }
//     // Again logging to category "EQUITY.NASD".
//     // [*] ...
// }
// ```
// Within `logIt`, a requisite logging category is visible at each of the
// locations marked by `[*]`.
//
///Example 3: C++ I/O Streams-Style Logging Macros
///- - - - - - - - - - - - - - - - - - - - - - - -
// The preferred logging method we use, the `iostream`-style macros such as
// `BALL_LOG_INFO`, allow streaming via the `bsl::ostream` `class` and the C++
// stream operator `<<`.  An advantage the C++ streaming style has over the
// `printf` style output (shown below in example 4) is that complex types often
// have the `operator<<(ostream&, const TYPE&)` function overloaded so that
// they are able to be easily streamed to output.  We demonstrate this here
// using C++ streaming to stream a `bdlt::Date` to output:
// ```
// int         lotSize = 400;
// const char *ticker  = "SUNW";
// double      price   = 5.65;
//
// // Trading on a market that settles 3 days in the future.
//
// bdlt::Date settle = bdlt::CurrentTime::local().date() + 3;
//
// BALL_LOG_SET_CATEGORY("EQUITY.NASD")
// ```
// We are logging with category "EQUITY.NASD", which is configured for a
// pass-through level of `e_INFO`, from here on.  We output a line using the
// `BALL_LOG_INFO` macro:
// ```
// BALL_LOG_INFO << "[1] " << lotSize
//               << " shares of " << ticker
//               << " sold at " << price
//               << " settlement date " << settle;
// ```
// The above results in the following single-line message being output:
// ```
// <ts> <pid> <tid> INFO x.cpp 1161 EQUITY.NASD [1] 400 shares of SUNW sold
// at 5.65 settlement date 17FEB2017
// ```
// `<ts>` is the timestamp, `<pid>` is the process id, `<tid>` is the thread
// id, `x.cpp` is the expansion of the `__FILE__` macro that is the name of the
// source file containing the call, 1161 is the line number of the call, and
// the trailing date following "settlement date" is the value of `settle`.
//
// Next, we set the category to "EQUITY.NASD.SUNW", which has been defined with
// `ball::Administration::addCategory` with its pass-through level set to
// `e_INFO` and the trigger levels set at or above `e_ERROR`, so a level of
// `e_WARN` also passes through:
// ```
// {
//     BALL_LOG_SET_CATEGORY("EQUITY.NASD.SUNW")
//
//     // Now logging with category "EQUITY.NASD.SUNW".
//
//     BALL_LOG_WARN << "[2] " << lotSize
//                   << " shares of " << ticker
//                   << " sold at " << price
//                   << " settlement date " << settle;
// }
// ```
// The above results in the following message to category "EQUITY.NASD.SUNW":
// ```
// <ts> <pid> <tid> WARN x.cpp 1185 EQUITY.NASD.SUNW [2] 400 shares of SUNW
// sold at 5.65 settlement date 17FEB2017
// ```
// Now, the category "EQUITY.NASD.SUNW" just went out of scope and category
// "EQUITY.NASD" is visible again, so it applies to the following:
// ```
// BALL_LOG_INFO << "[3] " << lotSize
//               << " shares of " << ticker
//               << " sold at " << price
//               << " settlement date " << settle;
// ```
// Finally, the above results in the following single-line message being
// output:
// ```
// <ts> <pid> <tid> INFO x.cpp 1198 EQUITY.NASD [3] 400 shares of SUNW sold
// at 5.65 settlement date 17FEB2017
// ```
// The settlement date was appended to the message as a simple illustration of
// the added flexibility provided by the C++ stream-based macros.  This last
// message was logged to category "EQUITY.NASD" at severity level
// `ball::Severity::e_INFO`.
//
// The C++ stream-based macros, as opposed to the `printf`-style macros, ensure
// at compile-time that no run-time format mismatches will occur.  Use of the
// stream-based logging style exclusively will likely lead to clearer, more
// maintainable code with fewer initial defects.
//
// Note that all uses of the log-generating macros, both `printf`-style and C++
// stream-based, *must* occur within function scope (i.e., not at file scope).
//
///Example 4: `printf`-Style Output
/// - - - - - - - - - - - - - - - -
// In the following example, we expand the `logIt` function (defined above) to
// log two messages using the `BALL_LOGVA_INFO` logging macro provided by this
// component.  This variadic macro takes a format string and a variable-length
// series of arguments, similar to `printf`.
// ```
// int         lotSize = 400;
// const char *ticker  = "SUNW";
// double      price   = 5.65;
//
// // Trading on a market that settles 3 days in the future.
//
// bdlt::Date settleDate = bdlt::CurrentTime::local().date() + 3;
// ```
// Because we can't easily `printf` complex types like `bdlt::Date` or
// `bsl::string`, we have to convert `settleDate` to a `const char *`
// ourselves.  Note that all this additional work was unnecessary in Example 3
// when we used the C++ `iostream`-style, rather than the `printf`-style,
// macros.
// ```
// bsl::ostringstream  settleOss;
// settleOss << settleDate;
// const bsl::string&  settleStr = settleOss.str();
// const char         *settle    = settleStr.c_str();
// ```
// We set logging with category "EQUITY.NASD", which was configured for a
// pass-through severity level of `e_INFO`, and call `BALL_LOGVA_INFO` to print
// our trade:
// ```
// BALL_LOG_SET_CATEGORY("EQUITY.NASD")
//
// BALL_LOGVA_INFO("[4] %d shares of %s sold at %f settlement date %s\n",
//                 lotSize, ticker, price, settle);
// ```
// The above results in the following single-line message being output to
// category "EQUITY.NASD.SUNW" at severity level `ball::Severity::e_INFO`:
// ```
// <ts> <pid> <tid> INFO x.cpp 1256 EQUITY.NASD [4] 400 shares of SUNW sold
// at 5.650000 settlement date 17FEB2017
// ```
// In the above, `<ts>` is the timestamp, `<pid>` is the process id, `<tid>` is
// the thread id, `x.cpp` is the expansion of the `__FILE__` macro that is the
// name of the source file containing the call, and 1256 is the line number of
// the call.
//
// Note that the first argument supplied to the `BALL_LOGVA_INFO` macro is a
// `printf`-style format specification.
//
// Next, we set the category to "EQUITY.NASD.SUNW", which is configured for a
// pass-through severity level of `e_INFO`:
// ```
// {
//     BALL_LOG_SET_CATEGORY("EQUITY.NASD.SUNW")
//
//     // Now logging with category "EQUITY.NASD.SUNW".
//
//     BALL_LOGVA_WARN("[5] %d shares of %s sold at %f settlement date %s\n",
//                     lotSize, ticker, price, settle);
// }
// ```
// The above results in the following single-line message to category
// "EQUITY.NASD.SUNW":
// ```
// <ts> <pid> <tid> WARN x.cpp 1281 EQUITY.NASD.SUNW [5] 400 shares of SUNW
// sold at 5.650000 settlement date 17FEB2017
// ```
// Now, the category "EQUITY.NASD.SUNW" just went out of scope and category
// "EQUITY.NASD" is visible again, so it applies to the following:
// ```
// BALL_LOGVA_INFO("[6] %d shares of %s sold at %f settlement date %s\n",
//                 lotSize, ticker, price, settle);
// ```
// Finally, the above results in the following single-line message being
// output:
// ```
// <ts> <pid> <tid> INFO x.cpp 1294 EQUITY.NASD [6] 400 shares of SUNW sold
// at 5.650000 settlement date 17FEB2017
// ```
//
///Example 5: Dynamic Categories
///- - - - - - - - - - - - - - -
// Logging must sometimes be controlled by parameters that are not available
// until run-time.  The `BALL_LOG_SET_DYNAMIC_CATEGORY` macro sets a category
// each time it is invoked (unlike `BALL_LOG_SET_CATEGORY`, which sets a
// category only on the first invocation and uses the cached address of the
// category on subsequent invocations).  The category name in the following
// `processSecurity` function is a combination of a static prefix and the
// (dynamic) `exchange` argument:
// ```
// void processSecurity(const char *security, const char *exchange)
// {
//     bsl::string categoryName("EXCHANGE:");
//     categoryName.append(exchange);
//
//     BALL_LOG_SET_DYNAMIC_CATEGORY(categoryName.c_str());
//
//     BALL_LOG_TRACE << "processing: " << security;
//
//     // ...
// }
// ```
// Now logging can be controlled independently for each `exchange`.
//
// *WARNING*: Along with the added flexibility provided by dynamic categories
// comes the additional overhead of computing and setting a category on each
// invocation.  Consequently, dynamic categories should be used *SPARINGLY* in
// most applications.
//
///Example 6: Rule-Based Logging
///- - - - - - - - - - - - - - -
// The following example demonstrates the use of attributes and rules to
// conditionally enable logging.
//
// We start by defining a function, `processData`, that is passed data in a
// `vector<char>` and information about the user who sent the data.  This
// example function performs no actual processing, but does log a single
// message at the `ball::Severity::e_DEBUG` threshold level.  The `processData`
// function also adds the user information passed to this function to the
// thread's attribute context.  We will use these attributes later, to create a
// logging rule that enables verbose logging only for a particular user.
// ```
// /// Process the specified `data` associated with the specified Bloomberg
// /// `uuid`, `luw`, and `terminalNumber`.
// void processData(int                      uuid,
//                  int                      luw,
//                  int                      terminalNumber,
//                  const bsl::vector<char>& data)
// {
//     (void)data;  // suppress "unused" warning
// ```
// We add our attributes using `ball::ScopedAttribute`, which adds an attribute
// container with one attribute to a list of containers.  This is easy and
// efficient if the number of attributes is small, but should not be used if
// there are a large number of attributes.  If motivated, we could use
// `ball::DefaultAttributeContainer`, which provides an efficient container for
// a large number of attributes, or even create a more efficient attribute
// container implementation specifically for these three attributes (uuid, luw,
// and terminalNumber).  See {`ball_scopedattributes`} (plural) for an example
// of using a different attribute container, and {`ball_attributecontainer`}
// for an example of creating a custom attribute container.
// ```
//     // We use `ball::ScopedAttribute` here because the number of
//     // attributes is relatively small.
//
//     ball::ScopedAttribute uuidAttribute("mylibrary.uuid", uuid);
//     ball::ScopedAttribute luwAttribute("mylibrary.luw", luw);
//     ball::ScopedAttribute termNumAttribute("mylibrary.terminalNumber",
//                                            terminalNumber);
// ```
// In this simplified example we perform no actual processing, and simply log
// a message at the `ball::Severity::e_DEBUG` level.
// ```
//     BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY");
//
//     BALL_LOG_DEBUG << "An example message";
// ```
// Notice that if we were not using a "scoped" attribute container like that
// provided automatically by `ball::ScopedAttribute` (e.g., if we were using a
// local `ball::DefaultAttributeContainer` instead), then the container
// **must** be removed from the `ball::AttributeContext` before it is
// destroyed!  See `ball_scopedattributes` (plural) for an example.
// ```
// }
// ```
// Next we demonstrate how to create a logging rule that sets the pass-through
// logging threshold to `ball::Severity::e_TRACE` (i.e., enables verbose
// logging) for a particular user when calling the `processData` function
// defined above.
//
// We start by creating the singleton logger manager that we configure with
// the stream observer and a default configuration.  We then call the
// `processData` function: This first call to `processData` will not result in
// any logged messages because `processData` logs its message at the
// `ball::Severity::e_DEBUG` level, which is below the default configured
// logging threshold.
// ```
// ball::LoggerManagerConfiguration lmConfig;
// ball::LoggerManagerScopedGuard   lmGuard(lmConfig);
//
// bsl::shared_ptr<ball::StreamObserver> observer =
//                         bsl::make_shared<ball::StreamObserver>(&bsl::cout);
//
// ball::LoggerManager::singleton().registerObserver(observer, "default");
//
// BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY");
//
// bsl::vector<char> message;
//
// BALL_LOG_ERROR << "Processing the first message.";
// processData(3938908, 2, 9001, message);
// ```
// Now we add a logging rule, setting the pass-through threshold to be
// `ball::Severity::e_TRACE` (i.e., enabling verbose logging) if the thread's
// context contains an attribute with name "mylibrary.uuid" with value 3938908.
// Note that we use the wild-card value `*` for the category so that the
// `ball::Rule` rule will apply to all categories.
// ```
// ball::Rule rule("*", 0, ball::Severity::e_TRACE, 0, 0);
// rule.addAttribute(ball::ManagedAttribute("mylibrary.uuid", 3938908));
// ball::LoggerManager::singleton().addRule(rule);
//
// BALL_LOG_ERROR << "Processing the second message.";
// processData(3938908, 2, 9001, message);
// ```
// The final call to the `processData` function below, passes a `uuid` of
// 2171395 (not 3938908) so the logging rule we defined will *not* apply and no
// message will be logged.
// ```
// BALL_LOG_ERROR << "Processing the third message.";
// processData(2171395, 2, 9001, message);
// ```
// The resulting logged output for this example looks like the following:
// ```
// ERROR example.cpp:105 EXAMPLE.CATEGORY Processing the first message.
// ERROR example.cpp:117 EXAMPLE.CATEGORY Processing the second message.
// DEBUG example.cpp:35 EXAMPLE.CATEGORY An example message
// ERROR example.cpp:129 EXAMPLE.CATEGORY Processing the third message.
// ```
//
///Example 7: Logging Using a Callback
///- - - - - - - - - - - - - - - - - -
// The following example demonstrates how to register a logging callback.  The
// C++ stream-based macros that take a callback are particularly useful to
// seamlessly populate the user fields of a record, thus simplifying the
// logging line.
//
// We define a callback function `populateUsingPoint` that appends to the
// specified `fields` the attributes of the `point` to log:
// ```
// /// Append to the specified `list` the name, x value, and y value of
// /// the specified `point`.
// void populateUsingPoint(ball::UserFields *fields, const Point& point)
// {
//     fields->appendString(point.name());
//     fields->appendInt64(point.x());
//     fields->appendInt64(point.y());
// }
//
// int validatePoint(const Point& point)
// {
//     BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY");
// ```
// We now bind our callback function `populateUsingPoint` and the supplied
// `point` to a functor object we will pass to the logging callback.  Note
// that the callback supplied to the logging macro must match the prototype
// `void (*)(ball::UserFields *)`.
// ```
//     bsl::function <void(ball::UserFields *)> callback;
//     callback = bdlf::BindUtil::bind(&populateUsingPoint,
//                                     bdlf::PlaceHolders::_1,
//                                     point);
//
//     int numErrors = 0;
//     if (point.x() > 255) {
//         BALL_LOGCB_ERROR(callback) << "X > 255";
//         ++numErrors;
//     }
//     if (point.x() < -255) {
//         BALL_LOGCB_ERROR(callback) << "X < -255";
//         ++numErrors;
//     }
//     if (point.y() > 255) {
//         BALL_LOGCB_ERROR(callback) << "Y > 255";
//         ++numErrors;
//     }
//     if (point.y() < -255) {
//         BALL_LOGCB_ERROR(callback) << "Y < -255";
//         ++numErrors;
//     }
//     return numErrors;
// }
// ```
//
///Example 8: Class-Scope Logging
/// - - - - - - - - - - - - - - -
// The following example demonstrates how to define and use logging categories
// that have class scope.
//
// First, we define a class, `Thing`, for which we want to do class-scope
// logging.  The use of the `BALL_LOG_SET_CLASS_CATEGORY` macro generates the
// requisite declarations within the definition of the class.  We have used the
// macro in a `private` section of the interface, which should be preferred,
// but `public` (or `protected`) is fine, too:
// ```
// // pckg_thing.h
// namespace pckg {
//
// class Thing {
//     // ...
//
//   private:
//     BALL_LOG_SET_CLASS_CATEGORY("PCKG.THING");
//
//   public:
//     // ...
//
//     // MANIPULATORS
//
//     /// Log to the class-scope category "PCKG.THING" if the specified
//     /// `useClassCategory` flag is `true`, and to the block-scope
//     /// category "X.Y.Z" otherwise.
//     void outOfLineMethodThatLogs(bool useClassCategory);
//
//     // ...
//
//     // ACCESSORS
//
//     /// Log a record to the class-scope category "PCKG.THING".
//     void inlineMethodThatLogs() const;
// };
// ```
// Next, we define the `inlineMethodThatLogs` method `inline` within the header
// file and log to the class-scope category using `BALL_LOG_TRACE`.  Since
// there is no other category in scope, the record is necessarily logged to the
// "PCKG.THING" category that is within the scope of the `Thing` class:
// ```
// // ...
//
// // ACCESSORS
// inline
// void Thing::inlineMethodThatLogs() const
// {
//     BALL_LOG_TRACE << "log to PCKG.THING";
// }
//
// }  // close namespace pckg
// ```
// Now, we define the `outOfLineMethodThatLogs` method within the `.cpp` file.
// On each invocation, this method logs one record using `BALL_LOG_TRACE`.  It
// logs to the "PCKG.THING" class-scope category if `useClassCategory` is
// `true`, and logs to the "X.Y.Z" block-scope category otherwise:
// ```
// // pckg_thing.cpp
// namespace pckg {
//
// // ...
//
// // MANIPULATORS
// void Thing::outOfLineMethodThatLogs(bool useClassCategory)
// {
//     if (useClassCategory) {
//         BALL_LOG_TRACE << "log to PCKG.THING";
//     }
//     else {
//         BALL_LOG_SET_CATEGORY("X.Y.Z");
//         BALL_LOG_TRACE << "log to X.Y.Z";
//     }
// }
//
// }  // close namespace pckg
// ```
// Finally, note that both block-scope and class-scope categories can be logged
// to within the same block.  For example, the following block within a `Thing`
// method would first log to "PCKG.THING" then log to "X.Y.Z":
// ```
//     {
//         BALL_LOG_TRACE << "log to PCKG.THING";
//
//         BALL_LOG_SET_CATEGORY("X.Y.Z");
//
//         BALL_LOG_TRACE << "log to X.Y.Z";
//     }
// ```

#include <balscm_version.h>

#include <ball_category.h>
#include <ball_categorymanager.h>
#include <ball_loggermanager.h>
#include <ball_severity.h>

#include <bslma_managedptr.h>

#include <bsls_annotation.h>
#include <bsls_keyword.h>
#include <bsls_performancehint.h>
#include <bsls_platform.h>

#include <bsl_ostream.h>

#include <bslmt_mutex.h>

                       // =========================
                       // Logging Macro Definitions
                       // =========================

#define BALL_LOG_CATEGORY                                                     \
    (ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)->category())

#define BALL_LOG_THRESHOLD                                                    \
    (ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)->threshold())

#define BALL_LOG_RECORD (ball_log_lOg_StReAm.record())

#define BALL_LOG_OUTPUT_STREAM (ball_log_lOg_StReAm.stream())

                       // ===========================
                       // Block-Scope Category Macros
                       // ===========================

#define BALL_LOG_SET_CATEGORY(CATEGORY)                                       \
    static BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = {      \
        { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY },      \
                                                             { 0 }, { 0 }     \
    };                                                                        \
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!BALL_LOG_CATEGORY)) {          \
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                                   \
        BloombergLP::ball::Log::setCategory(&BALL_LOG_CATEGORYHOLDER,         \
                                            CATEGORY);                        \
    }

#define BALL_LOG_SET_CATEGORY_HIERARCHICALLY(CATEGORY)                        \
    static BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = {      \
        { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY },      \
                                                             { 0 }, { 0 }     \
    };                                                                        \
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!BALL_LOG_CATEGORY)) {          \
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                                   \
        BloombergLP::ball::Log::setCategoryHierarchically(                    \
                                            &BALL_LOG_CATEGORYHOLDER,         \
                                            CATEGORY);                        \
    }

#define BALL_LOG_SET_DYNAMIC_CATEGORY(CATEGORY)                               \
    const BloombergLP::ball::Category *BALL_LOG_DYNAMIC_CATEGORY =            \
                               BloombergLP::ball::Log::setCategory(CATEGORY); \
    BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = {             \
        { BloombergLP::ball::CategoryHolder::e_DYNAMIC_CATEGORY },            \
        { const_cast<BloombergLP::ball::Category *>(                          \
                                                BALL_LOG_DYNAMIC_CATEGORY) }, \
        { 0 }                                                                 \
    };

#define BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(CATEGORY)                \
    const BloombergLP::ball::Category *BALL_LOG_DYNAMIC_CATEGORY =            \
                 BloombergLP::ball::Log::setCategoryHierarchically(CATEGORY); \
    BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = {             \
        { BloombergLP::ball::CategoryHolder::e_DYNAMIC_CATEGORY },            \
        { const_cast<BloombergLP::ball::Category *>(                          \
                                                BALL_LOG_DYNAMIC_CATEGORY) }, \
        { 0 }                                                                 \
    };

                       // ===========================
                       // Class-Scope Category Macros
                       // ===========================

#define BALL_LOG_SET_CLASS_CATEGORY(CATEGORY)                                 \
    static                                                                    \
    const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(      \
                     const BloombergLP::ball::CategoryHolder& categoryHolder) \
    {                                                                         \
        return &categoryHolder;                                               \
    }                                                                         \
    static                                                                    \
    const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int)  \
    {                                                                         \
        static BloombergLP::ball::CategoryHolder holder = {                   \
            { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY },  \
                                                                 { 0 }, { 0 } \
        };                                                                    \
        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) {      \
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                               \
            BloombergLP::ball::Log::setCategory(&holder, CATEGORY);           \
        }                                                                     \
        return &holder;                                                       \
    }                                                                         \
    enum { BALL_LOG_CATEGORYHOLDER = 0 }

#define BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(CATEGORY)                  \
    static                                                                    \
    const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(      \
                     const BloombergLP::ball::CategoryHolder& categoryHolder) \
    {                                                                         \
        return &categoryHolder;                                               \
    }                                                                         \
    static                                                                    \
    const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int)  \
    {                                                                         \
        static BloombergLP::ball::CategoryHolder holder = {                   \
            { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY },  \
                                                                 { 0 }, { 0 } \
        };                                                                    \
        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) {      \
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                               \
            BloombergLP::ball::Log::setCategoryHierarchically(&holder,        \
                                                              CATEGORY);      \
        }                                                                     \
        return &holder;                                                       \
    }                                                                         \
    enum { BALL_LOG_CATEGORYHOLDER = 0 }

                       // ===============================
                       // Namespace-Scope Category Macros
                       // ===============================

#define BALL_LOG_SET_NAMESPACE_CATEGORY(CATEGORY)                             \
namespace {                                                                   \
    static                                                                    \
    const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int)  \
    {                                                                         \
        static BloombergLP::ball::CategoryHolder holder = {                   \
            { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY },  \
                                                                 { 0 }, { 0 } \
        };                                                                    \
        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) {      \
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                               \
            BloombergLP::ball::Log::setCategory(&holder, CATEGORY);           \
        }                                                                     \
        return &holder;                                                       \
    }                                                                         \
    enum { BALL_LOG_CATEGORYHOLDER = 0 };                                     \
}

#define BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(CATEGORY)              \
namespace {                                                                   \
    static                                                                    \
    const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int)  \
    {                                                                         \
        static BloombergLP::ball::CategoryHolder holder = {                   \
            { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY },  \
                                                                 { 0 }, { 0 } \
        };                                                                    \
        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) {      \
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                               \
            BloombergLP::ball::Log::setCategoryHierarchically(&holder,        \
                                                              CATEGORY);      \
        }                                                                     \
        return &holder;                                                       \
    }                                                                         \
    enum { BALL_LOG_CATEGORYHOLDER = 0 };                                     \
}

                 // ====================================
                 // Implementation Details: Do *NOT* Use
                 // ====================================

// BALL_LOG_STREAM_CONST_IMP requires its argument to be a compile-time
// constant.

#define BALL_LOG_STREAM_CONST_IMP(SEVERITY)                                   \
for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR =       \
               BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>(   \
                        ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)); \
     ball_log_cAtEgOrYhOlDeR;                                                 \
     )                                                                        \
for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm(                       \
                                         ball_log_cAtEgOrYhOlDeR->category(), \
                                         __FILE__,                            \
                                         __LINE__,                            \
                                         (SEVERITY));                         \
     ball_log_cAtEgOrYhOlDeR;                                                 \
     ball_log_cAtEgOrYhOlDeR = 0)

// BALL_LOG_STREAM_IMP allows its argument to be calculated at run-time, at a
// cost in performance.

#define BALL_LOG_STREAM_IMP(SEVERITY)                                         \
for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR =       \
                         ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \
     ball_log_cAtEgOrYhOlDeR                                                  \
     && ball_log_cAtEgOrYhOlDeR->threshold() >= (SEVERITY)                    \
     && BloombergLP::ball::Log::isCategoryEnabled(ball_log_cAtEgOrYhOlDeR,    \
                                                  (SEVERITY));                \
     )                                                                        \
for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm(                       \
                                         ball_log_cAtEgOrYhOlDeR->category(), \
                                         __FILE__,                            \
                                         __LINE__,                            \
                                         (SEVERITY));                         \
     ball_log_cAtEgOrYhOlDeR;                                                 \
     ball_log_cAtEgOrYhOlDeR = 0)

                       // =======================
                       // C++ stream-based macros
                       // =======================

#define BALL_LOG_STREAM(SEVERITY)                                             \
    BALL_LOG_STREAM_IMP((SEVERITY)) BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_TRACE                                                        \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE)           \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_DEBUG                                                        \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG)           \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_INFO                                                         \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO)            \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_WARN                                                         \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN)            \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_ERROR                                                        \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR)           \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_FATAL                                                        \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL)           \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOG_STREAM_BLOCK(SEVERITY) BALL_LOG_STREAM_IMP((SEVERITY))

#define BALL_LOG_TRACE_BLOCK                                                  \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE)

#define BALL_LOG_DEBUG_BLOCK                                                  \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG)

#define BALL_LOG_INFO_BLOCK                                                   \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO)

#define BALL_LOG_WARN_BLOCK                                                   \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN)

#define BALL_LOG_ERROR_BLOCK                                                  \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR)

#define BALL_LOG_FATAL_BLOCK                                                  \
    BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL)

#define BALL_LOG_END ""

                    // ====================================
                    // Implementation Details: Do *NOT* Use
                    // ====================================

// BALL_LOGCB_STREAM_CONST_IMP requires its first argument to be a compile-time
// constant.

#define BALL_LOGCB_STREAM_CONST_IMP(SEVERITY, CALLBACK)                       \
for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR =       \
               BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>(   \
                        ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)); \
     ball_log_cAtEgOrYhOlDeR;                                                 \
     )                                                                        \
for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm(                       \
                                         ball_log_cAtEgOrYhOlDeR->category(), \
                                         __FILE__,                            \
                                         __LINE__,                            \
                                         (SEVERITY));                         \
     ball_log_cAtEgOrYhOlDeR                                                  \
     && (CALLBACK(&BALL_LOG_RECORD->customFields()), true);                   \
     ball_log_cAtEgOrYhOlDeR = 0)

// BALL_LOGCB_STREAM_IMP allows its first argument to be calculated at
// run-time, at a cost in performance.

#define BALL_LOGCB_STREAM_IMP(SEVERITY, CALLBACK)                             \
for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR =       \
                         ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \
     ball_log_cAtEgOrYhOlDeR                                                  \
     && ball_log_cAtEgOrYhOlDeR->threshold() >= (SEVERITY)                    \
     && BloombergLP::ball::Log::isCategoryEnabled(ball_log_cAtEgOrYhOlDeR,    \
                                                  (SEVERITY));                \
     )                                                                        \
for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm(                       \
                                         ball_log_cAtEgOrYhOlDeR->category(), \
                                         __FILE__,                            \
                                         __LINE__,                            \
                                         (SEVERITY));                         \
     ball_log_cAtEgOrYhOlDeR                                                  \
     && (CALLBACK(&BALL_LOG_RECORD->customFields()), true);                   \
     ball_log_cAtEgOrYhOlDeR = 0)

               // ========================================
               // C++ stream-based macros using a callback
               // ========================================

#define BALL_LOGCB_STREAM(BALL_SEVERITY, CALLBACK)                            \
    BALL_LOGCB_STREAM_IMP((BALL_SEVERITY), (CALLBACK)) BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_TRACE(CALLBACK)                                            \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE,         \
                                (CALLBACK))                                   \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_DEBUG(CALLBACK)                                            \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG,         \
                                (CALLBACK))                                   \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_INFO(CALLBACK)                                             \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO,          \
                                (CALLBACK))                                   \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_WARN(CALLBACK)                                             \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN,          \
                                (CALLBACK))                                   \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_ERROR(CALLBACK)                                            \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR,         \
                                (CALLBACK))                                   \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_FATAL(CALLBACK)                                            \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL,         \
                                (CALLBACK))                                   \
    BALL_LOG_OUTPUT_STREAM

#define BALL_LOGCB_STREAM_BLOCK(BALL_SEVERITY, CALLBACK)                      \
    BALL_LOGCB_STREAM_IMP((BALL_SEVERITY), (CALLBACK))

#define BALL_LOGCB_TRACE_BLOCK(CALLBACK)                                      \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE,         \
                                (CALLBACK))

#define BALL_LOGCB_DEBUG_BLOCK(CALLBACK)                                      \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG,         \
                                (CALLBACK))

#define BALL_LOGCB_INFO_BLOCK(CALLBACK)                                       \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO,          \
                                (CALLBACK))

#define BALL_LOGCB_WARN_BLOCK(CALLBACK)                                       \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN,          \
                                (CALLBACK))

#define BALL_LOGCB_ERROR_BLOCK(CALLBACK)                                      \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR,         \
                                (CALLBACK))

#define BALL_LOGCB_FATAL_BLOCK(CALLBACK)                                      \
    BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL,         \
                                (CALLBACK))

#define BALL_LOGCB_END ""

                 // ====================================
                 // Implementation Details: Do *NOT* Use
                 // ====================================

// BALL_LOGVA_CONST_IMP requires its first argument to be a compile-time
// constant, while all the others may be variables.

#define BALL_LOGVA_CONST_IMP(SEVERITY, ...)                                   \
do {                                                                          \
    if (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR =    \
               BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>(   \
                      ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER))) { \
        BloombergLP::ball::Log_Formatter ball_log_fOrMaTtEr(                  \
                                       ball_log_cAtEgOrYhOlDeR->category(),   \
                                       __FILE__,                              \
                                       __LINE__,                              \
                                       (SEVERITY));                           \
        BloombergLP::ball::Log::format(ball_log_fOrMaTtEr.messageBuffer(),    \
                                       ball_log_fOrMaTtEr.messageBufferLen(), \
                                       __VA_ARGS__);                          \
    }                                                                         \
} while(0)

                       // =====================
                       // 'printf'-style macros
                       // =====================

// BALL_LOGVA allows all its arguments to be calculated at run-time, at a cost
// in performance.

#define BALL_LOGVA(SEVERITY, ...)                                             \
do {                                                                          \
    const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR =        \
                         ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \
    if (ball_log_cAtEgOrYhOlDeR->threshold() >= (SEVERITY) &&                 \
           BloombergLP::ball::Log::isCategoryEnabled(ball_log_cAtEgOrYhOlDeR, \
                                                     (SEVERITY))) {           \
        BloombergLP::ball::Log_Formatter ball_log_fOrMaTtEr(                  \
                                       ball_log_cAtEgOrYhOlDeR->category(),   \
                                       __FILE__,                              \
                                       __LINE__,                              \
                                       (SEVERITY));                           \
        BloombergLP::ball::Log::format(ball_log_fOrMaTtEr.messageBuffer(),    \
                                       ball_log_fOrMaTtEr.messageBufferLen(), \
                                       __VA_ARGS__);                          \
    }                                                                         \
} while(0)

#define BALL_LOGVA_TRACE(...)                                                 \
    BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, __VA_ARGS__)

#define BALL_LOGVA_DEBUG(...)                                                 \
    BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, __VA_ARGS__)

#define BALL_LOGVA_INFO( ...)                                                 \
    BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_INFO,  __VA_ARGS__)

#define BALL_LOGVA_WARN( ...)                                                 \
    BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_WARN,  __VA_ARGS__)

#define BALL_LOGVA_ERROR(...)                                                 \
    BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, __VA_ARGS__)

#define BALL_LOGVA_FATAL(...)                                                 \
    BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, __VA_ARGS__)

                       // ==============
                       // Utility Macros
                       // ==============

#define BALL_LOG_IS_ENABLED(SEVERITY)                                         \
    ((BALL_LOG_THRESHOLD >= (SEVERITY))                                       \
     && BloombergLP::ball::Log::isCategoryEnabled(                            \
                         ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER), \
                         (SEVERITY)))

namespace BloombergLP {


namespace ball {

// BDE_VERIFY pragma: push
// BDE_VERIFY pragma: -AQQ01
// BDE_VERIFY pragma: -AQS01
// BDE_VERIFY pragma: -FB01
// BDE_VERIFY pragma: -KS00
// BDE_VERIFY pragma: -TR04
// BDE_VERIFY pragma: -TR17

                       // =======================
                       // CategoryHolder Accessor
                       // =======================

/// Return the address of the specified `categoryHolder`.  Note that this
/// function facilitates consistent lookup of block-scope and class-scope
/// category holders (see "Logging Macro Reuse" in the "IMPLEMENTATION
/// NOTES" of the component implementation file for details).
inline
const BloombergLP::ball::CategoryHolder* ball_log_getCategoryHolder(
                       const BloombergLP::ball::CategoryHolder& categoryHolder)
{
    return &categoryHolder;
}

// BDE_VERIFY pragma: pop

class Record;

                         // ==========
                         // struct Log
                         // ==========

/// This `struct` provides a namespace for a suite of utility functions that
/// simplify usage of the `ball_loggermanager` component.  The direct use
/// of these utility functions is *strongly* discouraged.
struct Log {

    // CLASS METHODS

    /// Fill the specified `buffer` with at most the specified `numBytes`
    /// characters produced by formatting the variable argument list
    /// according to the specified `printf`-style `format` argument; return
    /// the number of characters in the resulting formatted string.  The
    /// last character placed into `buffer` is always a null terminator
    /// (leaving at most `numBytes - 1` bytes of formatted data).  If
    /// `numBytes` is insufficient for the entire formatted string, this
    /// method fills `buffer` with the initial `numBytes - 1` bytes of
    /// formatted data followed by a null terminator and returns -1.  Note
    /// that with the exception of the return value, the behavior of this
    /// function exactly matches that of the C99 function `snprintf`.  Also
    /// note that `snprintf` is not part of standard C++-98, so its
    /// functionality is provided here.
    static int format(char        *buffer,
                      bsl::size_t  numBytes,
                      const char  *format, ...) BSLS_ANNOTATION_PRINTF(3, 4);

    /// Return the address of a modifiable record having the specified
    /// `fileName` and `lineNumber` attributes.  The memory for the record
    /// will be supplied by the allocator held by the logger manager
    /// singleton if the specified `category` is non-null, or by the
    /// currently installed default allocator otherwise.  The behavior is
    /// undefined unless the logger manager singleton is initialized when
    /// `category` is non-null.  Note that the returned `Record` must
    /// subsequently be supplied to a call to the 3-argument `logMessage`
    /// method.
    static Record *getRecord(const Category          *category,
                             const bsl::string_view&  fileName,
                             int                      lineNumber);

    /// Log a record containing the specified `message` text, `fileName`,
    /// `lineNumber`, `severity`, and the name of the specified `category`.
    /// (See the component-level documentation of `ball_record` for more
    /// information on the additional fields that are logged.)  Store the
    /// record in the buffer held by the logger if `severity` is at least
    /// as severe as the current "Record" threshold level of `category`.
    /// Pass the record directly to the registered observer if `severity`
    /// is at least as severe as the current "Pass" threshold level of
    /// `category`.  Publish the entire contents of the buffer of the
    /// logger if `severity` is at least as severe as the current "Trigger"
    /// threshold level of `category`.  Publish the entire contents of all
    /// buffers of all loggers if `severity` is at least as severe as the
    /// current "Trigger-All" threshold level of `category` (i.e., via the
    /// callback supplied at construction of the logger manager singleton).
    /// This method has no effect if `category` is 0 or `severity` is less
    /// severe than each of the threshold levels of `category`.  The
    /// behavior is undefined unless `severity` is in the range `[1 .. 255]`
    /// and the logger manager singleton is initialized when `category` is
    /// non-null.
    static void logMessage(const Category          *category,
                           int                      severity,
                           const bsl::string_view&  fileName,
                           int                      lineNumber,
                           const bsl::string_view&  message);

    /// Log the specified `record` after setting its category attribute to
    /// the specified `category` and its severity attribute to the specified
    /// `severity`.  (See the component-level documentation of `ball_record`
    /// for more information on the fields that are logged.)  Store the
    /// record in the buffer held by the logger if `severity` is at least
    /// as severe as the current "Record" threshold level of `category`.
    /// Pass the record directly to the registered observer if `severity`
    /// is at least as severe as the current "Pass" threshold level of
    /// `category`.  Publish the entire contents of the buffer of the
    /// logger if `severity` is at least as severe as the current "Trigger"
    /// threshold level of `category`.  Publish the entire contents of all
    /// buffers of all loggers if `severity` is at least as severe as the
    /// current "Trigger-All" threshold level of `category` (i.e., via the
    /// callback supplied at construction of the logger manager singleton).
    /// Finally, dispose of `record`.  This method has no effect (other than
    /// disposing of `record`) if `severity` is less severe than each of the
    /// threshold levels of `category`.  The behavior is undefined unless
    /// `severity` is in the range `[1 .. 255]`, `record` was obtained by a
    /// call to `Log::getRecord`, and, if `category` is not 0, the logger
    /// manager singleton is initialized.  Note that `record` will be
    /// invalid after this method returns.
    static void logMessage(const Category *category,
                           int             severity,
                           Record         *record);

    /// Block until access to the buffer used for formatting messages in
    /// this thread of execution is available.  Return the address of the
    /// modifiable buffer to which this thread of execution has exclusive
    /// access, load the address of the mutex that protects the buffer into
    /// the specified `*mutex` address, and load the size (in bytes) of the
    /// buffer into the specified `bufferSize` address.  The address remains
    /// valid, and the buffer remains locked by this thread of execution,
    /// until the `Log::releaseMessageBuffer` method is called.  The
    /// behavior is undefined if this thread of execution currently holds a
    /// lock on the buffer.  Note that the buffer is intended to be used
    /// *only* for formatting log messages immediately before a call to
    /// `Log::logMessage`; other use may adversely affect performance for
    /// the entire program.
    static char *obtainMessageBuffer(bslmt::Mutex **mutex,
                                     int           *bufferSize);

    /// Unlock the specified `mutex` that guards the buffer used for
    /// formatting messages in this thread of execution.  The behavior is
    /// undefined unless `mutex` was obtained by a call to
    /// `Log::obtainMessageBuffer` and has not yet been unlocked.
    static void releaseMessageBuffer(bslmt::Mutex *mutex);

    /// Return a managed pointer that refers to the memory block to which
    /// this thread of execution has exclusive access and load the size (in
    /// bytes) of this buffer into the specified `bufferSize` address.  Note
    /// that this method is intended for *internal* *use* only.
    static bslma::ManagedPtr<char> obtainMessageBuffer(int *bufferSize);

    /// Return from the logger manager singleton's category registry the
    /// address of the non-modifiable category having the specified
    /// `categoryName` if such a category exists, or if a new category
    /// having `categoryName` can be added to the registry (i.e., if the
    /// registry has sufficient capacity to accommodate new entries);
    /// otherwise, return the address of the non-modifiable *Default*
    /// *Category*.  Return 0 if the logger manager singleton is not
    /// initialized.
    static const Category *setCategory(const char *categoryName);

    /// Return the specified `categoryHolder` if the severity warrants
    /// logging according to the specified `t_SEVERITY` and
    /// `categoryHolder`, and 0 otherwise.
    template <int t_SEVERITY>
    static const CategoryHolder *categoryHolderIfEnabled(
                                         const CategoryHolder *categoryHolder);

    /// Load into the specified `categoryHolder` the address of the
    /// non-modifiable category having the specified `categoryName` if such
    /// a category exists, or if a new category having `categoryName` can
    /// be added to the registry (i.e., if the registry has sufficient
    /// capacity to accommodate new entries); otherwise, load the address of
    /// the non-modifiable *Default* *Category*.  Also load into
    /// `categoryHolder` the maximum threshold level of the category
    /// ultimately loaded into `categoryHolder`.  This method has no effect
    /// if the logger manager singleton is not initialized.
    static void setCategory(CategoryHolder *categoryHolder,
                            const char     *categoryName);

    /// Return from the logger manager singleton's category registry the
    /// address of the non-modifiable category having the specified
    /// `categoryName`, or, if no such category exists, add a new category
    /// having `categoryName` to the registry if possible (i.e., if the
    /// registry has sufficient capacity to accommodate new entries);
    /// otherwise, return the address of the non-modifiable *Default*
    /// *Category*.  If the logger manager singleton is not initialized,
    /// return 0 with no effect.  If a new category is created, it will have
    /// the same threshold levels as the category in the logger manager
    /// singleton whose name is the longest non-empty prefix of
    /// `categoryName` if such a category exists, and the threshold levels
    /// will be set as if `setCategory` had been called otherwise.
    static const Category *setCategoryHierarchically(const char *categoryName);

    /// Return from the logger manager singleton's category registry the
    /// address of the non-modifiable category having the specified
    /// `categoryName`, or, if no such category exists, add a new category
    /// having `categoryName` to the registry if possible (i.e., if the
    /// registry has sufficient capacity to accommodate new entries);
    /// otherwise, return the address of the non-modifiable *Default*
    /// *Category*.  If the logger manager singleton is not initialized,
    /// return 0 with no effect.  If a new category is created, it will have
    /// the same threshold levels as the category in the logger manager
    /// singleton whose name is the longest non-empty prefix of
    /// `categoryName` if such a category exists, and the threshold levels
    /// will be set as if `setCategory` had been called otherwise.  If the
    /// specified `categoryHolder` is non-zero, load it with the address of
    /// the returned category and the maximum threshold level of that
    /// category, and link `categoryHolder` to the other holders (if any)
    /// that currently reference the category.  Note that this method has
    /// the same effect on the logger manager singleton's category registry
    /// as the one-argument `setCategoryHierarchically` regardless of
    /// whether `0 == categoryHolder`.
    static const Category *setCategoryHierarchically(
                                                CategoryHolder *categoryHolder,
                                                const char     *categoryName);

    /// Return `true` if logging to the category associated with the
    /// specified `categoryHolder` at the specified `severity` is enabled,
    /// or if `Severity::e_WARN >= severity` and the logger manager
    /// singleton is not initialized; return `false` otherwise.
    static bool isCategoryEnabled(const CategoryHolder *categoryHolder,
                                  int                   severity);
};

                        // ================
                        // class Log_Stream
                        // ================

/// This class provides an aggregate of several objects relevant to the
/// logging of a message via the C++ stream-based macros:
/// ```
/// - record to be logged
/// - category to which to log the record
/// - severity at which to log the record
/// - stream to which the user log message is put
/// ```
/// As a side-effect of creating an object of this class, the record and
/// stream are also constructed.  As a side-effect of destroying the
/// object, the record is logged.
///
/// This class should *not* be used directly by client code.  It is an
/// implementation detail of the macros provided by this component.
class Log_Stream {

    // DATA
    const Category *d_category_p;  // category to which record is logged
                                   // (held, not owned)

    Record         *d_record_p;    // logged record (held, not owned)

    const int       d_severity;    // severity at which record is logged

  private:
    // NOT IMPLEMENTED
    Log_Stream(const Log_Stream&);
    Log_Stream& operator=(const Log_Stream&);

  public:
    // CREATORS

    /// Create a logging stream that holds (1) the specified `category` and
    /// `severity`, (2) a record that is created from the specified
    /// `fileName` and `lineNumber`, and (3) an `bsl::ostream` to which the
    /// log message is put.
    Log_Stream(const Category          *category,
               const bsl::string_view&  fileName,
               int                      lineNumber,
               int                      severity);

    /// Log the record held by this logging stream to the held category (as
    /// returned by `category`) at the held severity (as returned by
    /// `severity`) and destroy this logging stream.
    ~Log_Stream() BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(false);

    // MANIPULATORS

    /// Return the address of the modifiable log record held by this logging
    /// stream.  The address remains valid until this logging stream is
    /// destroyed.
    Record *record();

    /// Return a reference to the modifiable stream held by this logging
    /// stream.  The reference remains valid until this logging stream is
    /// destroyed.
    bsl::ostream& stream();

    // ACCESSORS

    /// Return the address of the non-modifiable category held by this
    /// logging stream.
    const Category *category() const;

    /// Return the address of the non-modifiable log record held by this
    /// logging stream.  The address remains valid until this logging stream
    /// is destroyed.
    const Record *record() const;

    /// Return the severity held by this logging stream.
    int severity() const;
};

                     // ===================
                     // class Log_Formatter
                     // ===================

/// This class provides an aggregate of several objects relevant to the
/// logging of a message via the `printf`-style macros:
/// ```
/// - record to be logged
/// - category to which to log the record
/// - severity at which to log the record
/// - buffer in which the user log message is formatted
/// ```
/// As a side-effect of creating an object of this class, the record is
/// constructed, and the buffer is obtained.  As a side-effect of destroying
/// the object, the record is formatted, using the buffer, and logged.
///
/// This class should *not* be used directly by client code.  It is an
/// implementation detail of the macros provided by this component.
class Log_Formatter {

    // DATA
    const Category          *d_category_p;  // category to which record is
                                            // logged (held, not owned)

    Record                  *d_record_p;    // logged record (held, not owned)

    const int                d_severity;    // severity at which record is
                                            // logged

    int                      d_bufferLen;   // length of buffer

    bslma::ManagedPtr<char>  d_buffer;      // buffer for formatted user log
                                            // message

  private:
    // NOT IMPLEMENTED
    Log_Formatter(const Log_Formatter&);
    Log_Formatter& operator=(const Log_Formatter&);

  public:
    // CREATORS

    /// Create a logging formatter that holds (1) the specified `category`
    /// and `severity`, (2) a record that is created from the specified
    /// `fileName` and `lineNumber`, and (3) a buffer into which the log
    /// message is formatted.
    Log_Formatter(const Category          *category,
                  const bsl::string_view&  fileName,
                  int                      lineNumber,
                  int                      severity);

    /// Log the record held by this logging formatter to the held category
    /// (as returned by `category`) at the held severity (as returned by
    /// `severity`), and destroy this logging formatter.
    ~Log_Formatter();

    // MANIPULATORS

    /// Return the address of the modifiable buffer held by this logging
    /// formatter.  The address remains valid until this logging formatter
    /// is destroyed.
    char *messageBuffer();

    /// Return the address of the modifiable log record held by this logging
    /// formatter.  The address remains valid until this logging formatter
    /// is destroyed.
    Record *record();

    // ACCESSORS

    /// Return the address of the non-modifiable category held by this
    /// logging formatter.
    const Category *category() const;

    /// Return the length (in bytes) of the buffer held by this logging
    /// formatter.
    int messageBufferLen() const;

    /// Return the address of the non-modifiable log record held by this
    /// logging formatter.  The address remains valid until this logging
    /// formatter is destroyed.
    const Record *record() const;

    /// Return the severity held by this logging formatter.
    int severity() const;
};

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================

                                 // ----------
                                 // struct Log
                                 // ----------

// CLASS METHODS
template <int t_SEVERITY>
inline
const CategoryHolder *Log::categoryHolderIfEnabled(
                                          const CategoryHolder *categoryHolder)
{
    // The following condition is calculated at compile time so has no run-time
    // cost.  Code from the branch not taken will not be generated.  Note that
    // we expect TRACE and DEBUG messages not to be logged and thus they are
    // marked with unlikely performance hints.

    if (t_SEVERITY <= Severity::e_INFO) {
        if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(categoryHolder->threshold() >=
                                                t_SEVERITY)) {
            if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(
                         Log::isCategoryEnabled(categoryHolder, t_SEVERITY))) {
                return categoryHolder;                                // RETURN
            }
            else {
                BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
            }

            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        }
    }
    else {
        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
                                  categoryHolder->threshold() >= t_SEVERITY)) {
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;

            if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
                         Log::isCategoryEnabled(categoryHolder, t_SEVERITY))) {
                BSLS_PERFORMANCEHINT_UNLIKELY_HINT;

                return categoryHolder;                                // RETURN
            }
        }
    }

    return 0;
}

inline
const Category *Log::setCategoryHierarchically(const char *categoryName)
{
    return setCategoryHierarchically(0, categoryName);
}

                             // ----------------
                             // class Log_Stream
                             // ----------------

// MANIPULATORS
inline
Record *Log_Stream::record()
{
    return d_record_p;
}

inline
bsl::ostream& Log_Stream::stream()
{
    return d_record_p->fixedFields().messageStream();
}

// ACCESSORS
inline
const Category *Log_Stream::category() const
{
    return d_category_p;
}

inline
const Record *Log_Stream::record() const
{
    return d_record_p;
}

inline
int Log_Stream::severity() const
{
    return d_severity;
}

                     // -------------------
                     // class Log_Formatter
                     // -------------------

// MANIPULATORS
inline
char *Log_Formatter::messageBuffer()
{
    return d_buffer.get();
}

inline
Record *Log_Formatter::record()
{
    return d_record_p;
}

// ACCESSORS
inline
const Category *Log_Formatter::category() const
{
    return d_category_p;
}

inline
int Log_Formatter::messageBufferLen() const
{
    return d_bufferLen;
}

inline
const Record *Log_Formatter::record() const
{
    return d_record_p;
}

inline
int Log_Formatter::severity() const
{
    return d_severity;
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2017 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
